﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using CommandLine;
using CommandLine.Text;
using MediaSage.Lib.UIFramework.DataFile;
using Ionic.Zip;
using Ionic.Zlib;
using MediaSage.Lib.UIFramework.Theme;

namespace BuildThemePackage
{
    internal class Program
    {
        private static string DLLName;
        private static string ThemeName;
        private static bool Default = false;
        private static List<ThemeCapabilities> Capabilities;
        private static string UpdateURL;
        private static string ResourceDir;
        private static string[] IncludedFiles;
        private static string OutputDir;

        private static void Main(string[] args)
        {
            Console.WriteLine("Creating Theme Package:");
            Console.WriteLine(" - Checking CLI Args");
            Capabilities=new List<ThemeCapabilities>();
            ProcessCLI(args);
            Console.WriteLine(" - Testing package contents");
            if (!File.Exists(DLLName))
            {
                Console.WriteLine(" " + DLLName + " not found, aborting");
                Environment.Exit(1);
            } 
            Console.WriteLine("     - DLL: "+DLLName+" found");
            foreach (string file in IncludedFiles)
            {
                if (!File.Exists(file))
                {
                    Console.WriteLine(" File: " + file + " not found, exiting");
                    Environment.Exit(1);
                }
                else
                {
                    Console.WriteLine("     - Included File: " + file + " found");            
                }
            }
            if (!Directory.Exists(ResourceDir))
            {
                Console.WriteLine(" Resource Directory: " + ResourceDir + " not found, aborting");
                Environment.Exit(1);
            }
            else
            {
                Console.WriteLine("     - Resource Dir: " + ResourceDir + " found");            
            }
            Console.WriteLine(" - Getting Theme Version");
            ThemeData dataFile = new ThemeData();
            Assembly theme = null;
            try
            {
                theme = Assembly.LoadFrom(DLLName);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception loading Theme: " + e.Message);
                Environment.Exit(1);
            }
            if (theme == null)
            {
                Console.WriteLine("Theme incorrectly loaded, exiting");
                Environment.Exit(1);
            }
            Version version = theme.GetName().Version;
            dataFile.Version.Major = version.Major;
            dataFile.Version.Minor = version.Minor;
            dataFile.Version.BuildNumber = version.Build;
            Console.WriteLine(String.Format("     - Version: {0}.{1}.{2}",dataFile.Version.Major,dataFile.Version.Minor,dataFile.Version.BuildNumber));
            dataFile.DLL = Path.GetFileName(DLLName);
            string[] tmp = ResourceDir.Split(Path.DirectorySeparatorChar);
            int a = tmp.Length-1;
            while (String.IsNullOrEmpty(tmp[a]) && a > 0)
            {
                a--;
            }
            dataFile.ResourceDirectory = tmp[a];            
            dataFile.Capabilitiy = Capabilities;
            dataFile.Default = Default;
            dataFile.UpdateURL = UpdateURL;
            
            XmlSerializer serializer = new XmlSerializer(dataFile.GetType());

            string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            Console.WriteLine(" - Creating Temporary Dir: " + tempDir);
            try
            {
                Directory.CreateDirectory(tempDir);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception creating Temporary Dir, msg: " + e.Message);
                Environment.Exit(1);
            }

            Console.WriteLine(" - Serializing XML File");
            string xmlFilename = Path.Combine(tempDir, "THEME.XML");
            try
            {
                TextWriter tw = new StreamWriter(xmlFilename);
                serializer.Serialize(tw, dataFile);
                tw.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception serializing THEME.XML");
                Environment.Exit(1);
            }
            Console.WriteLine("- Copying files to temp location");
            try
            {
                Console.WriteLine("     - Copying " + DLLName);
                File.Copy(DLLName, Path.Combine(tempDir, dataFile.DLL));
                Directory.CreateDirectory(Path.Combine(tempDir, dataFile.ResourceDirectory));
                string resDir = Path.Combine(tempDir, dataFile.ResourceDirectory);
                string[] files = Directory.GetFiles(ResourceDir);
                foreach (string file in files)
                {
                    Console.WriteLine("     - Copying " + file);
                    File.Copy(Path.Combine(ResourceDir, file), Path.Combine(resDir, Path.GetFileName(file)));
                }
                foreach (string file in IncludedFiles)
                {
                    Console.WriteLine("     - Copying " + file);
                    File.Copy(file, Path.Combine(tempDir, Path.GetFileName(file)));
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception copying files: " + e.Message);
                Environment.Exit(1);
            }
            Console.WriteLine(" - Package Filename: "+ThemeName+".ZIP");
            
            Console.WriteLine(" - Creating " + Path.Combine(Path.Combine(tempDir, ThemeName + ".ZIP")));
            try
            {
                using (var zip = new ZipFile(Path.Combine(Path.Combine(tempDir, ThemeName + ".ZIP"))))
                {
                    zip.CompressionLevel = CompressionLevel.BestCompression;
                    Console.WriteLine("     - Adding THEME.XML");
                    zip.AddFile(Path.Combine(tempDir, "THEME.XML"), "");
                    Console.WriteLine("     - Adding " + Path.GetFileName(DLLName));
                    zip.AddFile(Path.Combine(tempDir, Path.GetFileName(DLLName)), "");
                    if (IncludedFiles != null)
                    {
                        foreach (string file in IncludedFiles)
                        {
                            Console.WriteLine("     - Adding " + file);
                            zip.AddFile(Path.Combine(tempDir, Path.GetFileName(file)), "");
                        }
                    }
                    Console.WriteLine("     - Adding " + Path.Combine(tempDir, ResourceDir));
                    
                    zip.AddDirectory(Path.Combine(tempDir,dataFile.ResourceDirectory),dataFile.ResourceDirectory);   
                    Console.WriteLine(" - Closing Zipfile");
                    zip.Save();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Failed to create Installation Package");
                Console.WriteLine("BuildThemePackage - ERROR: " + e.Message);
                Environment.Exit(1);
            }
            Console.WriteLine(" - Copying installation package to output directory");
            try
            {
                Console.WriteLine("     - Copying "+Path.Combine(tempDir,ThemeName+".ZIP"));
                Console.WriteLine("     - to " + Path.Combine(OutputDir, ThemeName + ".ZIP"));
                File.Copy(Path.Combine(tempDir, ThemeName + ".ZIP"), Path.Combine(OutputDir,ThemeName+".ZIP"), true);
            }
            catch (Exception e)
            {
                Console.WriteLine("Failed to copy installation package to output");
                Console.WriteLine("BuildPluginInstall - ERROR: " + e.Message);
                Environment.Exit(1);
            }
            
            Console.WriteLine(" - Cleaning up");
            try
            {
                Directory.Delete(tempDir, true);
            }
            catch (Exception e)
            {
                Console.WriteLine("Failed to clean up temp dir");
                Console.WriteLine("BuildPluginInstall - ERROR: " + e.Message);
                Environment.Exit(1);
            }
            Console.WriteLine("ALL DONE!");

        }


        private static
            void ProcessCLI(string[] args)
        {
            var options = new Options();
            var parser = new CommandLineParser(new CommandLineParserSettings(false, true, Console.Error));
            if (!parser.ParseArguments(args, options))
            {
                Environment.Exit(1);
            }
            DLLName = options.DLL;
            // TODO: Eventually programmatically call each of the methods and check to see if the method is implemented to figure out the capability

            foreach (string cap in options.Capabilities)
            {
                switch (cap.ToUpper())
                {
                    case("DIALOGWINDOW"):
                        Capabilities.Add(ThemeCapabilities.DialogWindow);
                        Console.WriteLine("     - Adding Capability: DialogWindow");
                        break;
                    case("DIALOGPAGE"):
                        Capabilities.Add(ThemeCapabilities.DialogPage);
                        Console.WriteLine("     - Adding Capability: DialogPage");
                        break;
                    case("MAINPAGE"):
                        Capabilities.Add(ThemeCapabilities.MainPage);
                        Console.WriteLine("     - Adding Capability: MainPage");
                        break;
                    case("NOTIFICATIONWINDOW"):
                        Capabilities.Add(ThemeCapabilities.NotificationWindow);
                        Console.WriteLine("     - Adding Capability: NotificationWindow");
                        break;
                    default:
                        Console.WriteLine("Unknown Capability: "+cap);
                        Console.WriteLine("Exiting");
                        Environment.Exit(1);
                        break;
                }

            }            
            Default=options.Default;
            ThemeName=options.Name;
            UpdateURL = options.UpdateURL;
            IncludedFiles = options.IncludedFiles;
            OutputDir = options.OutputDir;
            ResourceDir = options.ResourceDir;
        }


        internal sealed class Options : CommandLineOptionsBase
        {
            #region Properties

            [Option("u", "updateurl", HelpText = "URL the Theme gets updates from")]
            public string UpdateURL { get; set; }

            [Option("d", "dll", Required = true, HelpText = "Theme main DLL")]
            public string DLL { get; set; }

            [Option("n", "name", Required = true, HelpText = "Theme name")]
            public string Name { get; set; }
            
            [Option("f", null, HelpText="Mark as default Theme")]
            public bool Default { get; set; }

            [OptionArray("c", "capability", HelpText = "Define capability of Theme")]
            public string[] Capabilities { get; set; }            

            [Option("r", "resourceDirectory", HelpText = "The resource directory for the Theme")]
            public string ResourceDir { get; set; }

            [OptionArray("i", "include", HelpText = "Files to include with Theme")]
            public string[] IncludedFiles { get; set; }

            [Option("o", "outputDir", Required=true, HelpText = "Directory to put the theme package in (Name will be name.ZIP")]
            public string OutputDir { get; set; }

            #endregion

            [HelpOption]
            public string GetUsage()
            {
                var ht = new HelpText(new HeadingInfo("BuildThemePackage", "0.9"));
                string errors = ht.RenderParsingErrorsText(this, 2);
                // indent with two spaces
                if (!string.IsNullOrEmpty(errors))
                {
                    ht.AddPreOptionsLine(string.Concat(Environment.NewLine, "ERROR(S):"));
                    ht.AddPreOptionsLine(errors);
                }
                ht.AddOptions(this);
                return ht;
            }
        }
    }
}
